﻿using System.Reflection;
using Apex.EventBus.Connections;
using Apex.EventBus.EventBuses;
using Apex.EventBus.Handlers;
using Apex.EventBus.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;

namespace Apex.EventBus.Extensions
{
    public static class ServicesCollectionExtensions
    {
        /// <summary>
        /// 添加事件总线服务到 IServiceCollection 中
        /// </summary>
        /// <param name="services">IServiceCollection 实例</param>
        /// <param name="queueName">队列名称。如果多个消费者订阅同一个队列，消息会被平均分摊给多个消费者处理</param>
        /// <param name="assemblies">包含事件处理程序类型的程序集</param>
        /// <returns>已更新的 IServiceCollection 实例。</returns>
        public static IServiceCollection AddEventBus(this IServiceCollection services, string queueName, params Assembly[] assemblies)
        {
            return services.AddEventBus(queueName, assemblies.ToList());
        }

        /// <summary>
        /// 添加事件总线服务到 IServiceCollection 中
        /// </summary>
        /// <param name="services">IServiceCollection 实例</param>
        /// <param name="queueName">队列名称。</param>
        /// <param name="assemblies">包含事件处理程序类型的程序集。</param>
        /// <returns>已更新的 IServiceCollection 实例。</returns>
        public static IServiceCollection AddEventBus(this IServiceCollection services, string queueName, IEnumerable<Assembly> assemblies)
        {
            List<Type> eventHandlers = new List<Type>();
            foreach (var asm in assemblies)
            {
                // 获取程序集中所有非抽象且可分配给 IIntegrationEventHandler 接口的类型。
                var types = asm.GetTypes().Where(t => t.IsAbstract == false && t.IsAssignableTo(typeof(IIntegrationEventHandler)));
                eventHandlers.AddRange(types);
            }
            return services.AddEventBus(queueName, eventHandlers);
        }

        /// <summary>
        /// 添加事件总线服务到 IServiceCollection 中
        /// </summary>
        /// <param name="services">IServiceCollection 实例</param>
        /// <param name="queueName">队列名称</param>
        /// <param name="eventHandlerTypes">事件处理程序类型集合</param>
        /// <returns>已更新的 IServiceCollection 实例。</returns>
        public static IServiceCollection AddEventBus(this IServiceCollection services, string queueName, IEnumerable<Type> eventHandlerTypes)
        {
            foreach (Type type in eventHandlerTypes)
            {
                // 将事件处理程序类型注册为 Scoped 生命周期服务
                services.AddScoped(type, type);
            }

            services.AddSingleton<IEventBus>(sp =>
            {
                // 获取 RabbitMQ 配置选项
                var optionMQ = sp.GetRequiredService<IOptions<IntegrationEventRabbitMQOptions>>().Value;
                var factory = new ConnectionFactory()
                {
                    HostName = optionMQ.HostName,
                    DispatchConsumersAsync = true
                };
                if (optionMQ.UserName != null)
                {
                    factory.UserName = optionMQ.UserName;
                }
                if (optionMQ.Password != null)
                {
                    factory.Password = optionMQ.Password;
                }
                // 创建 RabbitMQ 连接
                RabbitMQConnection mqConnection = new RabbitMQConnection(factory);
                var serviceScopeFactory = sp.GetRequiredService<IServiceScopeFactory>();

                // 创建 RabbitMQ 事件总线
                var eventBus = new RabbitMQEventBus(mqConnection, serviceScopeFactory, optionMQ.ExchangeName, queueName);

                // 遍历所有事件处理程序类型，并订阅对应事件
                foreach (Type type in eventHandlerTypes)
                {
                    // 获取事件处理程序类型上的 EventNameAttribute，并根据其值订阅事件。
                    var eventNameAttrs = type.GetCustomAttributes<EventNameAttribute>();
                    if (eventNameAttrs.Any() == false)
                    {
                        //throw new ApplicationException($"There should be at least one EventNameAttribute on {type}");
                        throw new ApplicationException($"{type}上应该至少有一个EventName属性");
                    }
                    foreach (var eventNameAttr in eventNameAttrs)
                    {
                        eventBus.Subscribe(eventNameAttr.Name, type);
                    }
                }

                return eventBus;
            });
            return services;
        }
    }
}
